#include <stdio.h>
#include <stdlib.h>

#include "../hle.h"

#include "../blkalloc.h"
#include "__SysMemUserForUser.h"

typedef struct
{
    s32 uid;
    u32 address;
    char name[32];
} PartitionMemoryBlock;

void sceKernelMaxFreeMemSize() 
{
    u32 retVal = blkAlloc_getMaxBlk(&userMemory);
    _log(INF, HLE, "0x%08x = sceKernelMaxFreeMemSize()", retVal);
    RETURN(retVal);
}

void sceKernelTotalFreeMemSize()
{
    u32 retVal = blkAlloc_getFreeSize(&userMemory);
    _log(INF, HLE, "0x%08x = sceKernelTotalFreeMemSize()", retVal);
    RETURN(retVal);
}

void sceKernelAllocPartitionMemory()
{
    s32 partid = PARAM(0);
    char *name = mem_readStr(PARAM(1));
    s32 type = PARAM(2);
    u32 size = PARAM(3);
    s32 addr = PARAM(4); /* only if type == addr */
    SceUID id;

    PartitionMemoryBlock *block = malloc(sizeof(PartitionMemoryBlock));
    block->address = blkAlloc_alloc(&userMemory, size, type == 1, "Partition", 1);
    blkAlloc_listBlocks(&userMemory);
    if (block->address == 0)
    {
        _log(ERR, HLE, "sceKernelAllocPartitionMemory(partition = %d, %s, type = %d, size = 0x%08x, addr = %08x): allocation failed",
                                                              partid,        name, type,     size,      addr);
        RETURN(SCE_ERROR_NO_MEMORY);
        mem_freeStr(name);
        return;
    }

    id = ko_init(block, KO_PARTMEM);
    strncpy(block->name, name, 32);

    _log(INF, HLE, "%d = sceKernelAllocPartitionMemory(partition = %d, %s, type = %d, size = 0x%08x, addr = %08x)",
                    id,                        partid,        name, type,     size,      addr);
    if (type == 2)
        _log(ERR, HLE, "ARGH! sceKernelAllocPartMem wants a specific address");

    RETURN(id);
    mem_freeStr(name);
}

void sceKernelFreePartitionMemory()
{
    SceUID id = PARAM(0);
    PartitionMemoryBlock *block = ko_get(id);
    if (block)
    {
        _log(INF, HLE, "sceKernelFreePartitionMemory(%d)", id);
        blkAlloc_free(&userMemory, block->address);
        ko_free(id);
        RETURN(0);
    }
    else {
        _log(ERR, HLE, "sceKernelFreePartitionMemory(%d): bad partition", id);
        RETURN(SCE_ERROR_ILLEGAL_CHUNK_ID);
    }
}

void sceKernelGetBlockHeadAddr()
{
    SceUID id = PARAM(0);
    PartitionMemoryBlock *block = ko_get(id);
    if (block) {
        _log(INF, HLE, "%08x = sceKernelGetBlockHeadAddr(%d)", block->address, id);
        RETURN(block->address);
    }
    else {
        _log(ERR, HLE, "sceKernelGetBlockHeadAddr(%d): bad partition", id);
        RETURN(SCE_ERROR_ILLEGAL_CHUNK_ID);
    }
}

void sceKernelPrintf()
{
    char *str1 = mem_readStr(PARAM(0));
    char *str2 = mem_readStr(PARAM(1));
    _log(WRN, HLE, "sceKernelPrintf(\"%s\", %s, ...)", str1, str2);
    RETURN(0);
    mem_freeStr(str1);
    mem_freeStr(str2);
}

void sceKernelDevkitVersion()
{
    _log(INF, HLE, "sceKernelDevkitVersion()");
    RETURN(0x01050001); /* 1.50 */
}

void sceKernelSetCompilerVersion()
{
    _log(WRN, HLE, "sceKernelSetCompilerVersion(%08x, %08x)", PARAM(0), PARAM(1));
    RETURN(0);
}

void sceKernelSetCompiledSdkVersion()
{
    _log(WRN, HLE, "sceKernelSetCompiledSdkVersion(%08x, %08x, %08x)", PARAM(0), PARAM(1), PARAM(2));
    RETURN(0);
}

void sceKernelSetCompiledSdkVersion370()
{
    _log(WRN, HLE, "sceKernelSetCompiledSdkVersion370(%08x, %08x)", PARAM(0), PARAM(1));
    RETURN(0);
}

void sceKernelSetCompiledSdkVersion395()
{
    _log(WRN, HLE, "sceKernelSetCompiledSdkVersion395(%08x, %08x)", PARAM(0), PARAM(1));
    RETURN(0);
}

